﻿using System;
using System.Linq;
using System.Linq.Expressions;
using eCRAM.Model;

namespace eCRAM.Dao
{
    internal static class LinqTools
    {
        /// <summary>
        /// Updates Entity
        /// </summary>
        /// <param name="entity">Entity which hold the updated information</param>
        /// <param name="query">query to get the same
        /// entity from db and perform the update operation</param>
        /// <param name="context"></param>
        /// <remarks>this method will do dynamic property mapping between the passed entity
        /// and the entity retrieved from DB upon the query defined,
        /// ONLY ValueTypes and strings are
        /// mapped between both entities, NO nested objects will be mapped, you have to do
        /// the objects mapping nested in your entity before calling this method</remarks>
        internal static void Update<T>(T entity, Expression<Func<T, bool>> query, eCramDataContext context) where T : class
        {
            T entityFromDb = context.GetTable<T>().Where(query).SingleOrDefault();
            if (entityFromDb == null)
            {
                throw new NullReferenceException("Query Supplied to " + "Get entity from DB is invalid, NULL value returned");
            }

            var properties = entityFromDb.GetType().GetProperties();
            foreach (var property in properties)
            {
                object propertyValue = null;
                if (property.GetSetMethod() == null) continue;


                var entityProperty = entity.GetType().GetProperty(property.Name);
                if (entityProperty.PropertyType.BaseType == Type.GetType("System.ValueType")
                    || entityProperty.PropertyType == Type.GetType("System.String"))
                {
                    propertyValue = entity.GetType().GetProperty(property.Name).GetValue(entity, null);
                }
                if (propertyValue != null
                    || property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    property.SetValue(entityFromDb, propertyValue, null);
                }
            }

        }

        /// <summary>
        /// Fusionne 2 expression lambda
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="Y"></typeparam>
        /// <param name="criteria1"></param>
        /// <param name="criteria2"></param>
        /// <returns></returns>
        internal static Expression<Func<T, Y>> MergeCriteria<T, Y>(this Expression<Func<T, Y>> criteria1, Expression<Func<T, Y>> criteria2)
        {
            var param = Expression.Parameter(typeof(T), "item");
            var body = Expression.AndAlso(
                    Expression.Invoke(criteria1, param),
                    Expression.Invoke(criteria2, param)
                );
            return Expression.Lambda<Func<T, Y>>(body, param);
        }
    }
}
